package com.lemon.boot.common.filter;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.lemon.boot.common.consts.RedisKeyConst;
import com.lemon.boot.common.entity.LoginUserDetail;
import com.lemon.boot.common.properties.JwtConfigProperties;
import com.lemon.boot.common.properties.RememberMeConfigProperties;
import com.lemon.boot.common.service.RedisService;
import com.lemon.boot.common.utils.JwtUtil;
import com.lemon.boot.common.utils.RequestUtil;
import com.lemon.boot.common.utils.ResponseUtil;
import com.lemon.boot.common.utils.RsaUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * @author 李猛
 * @datetime 2024/2/26 15:37
 * @description 登录过滤器
 */
@Slf4j
public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    public AuthenticationManager authenticationManager;
    public RedisService redisService;
    public JwtConfigProperties jwtConfigProperties;
    public RememberMeConfigProperties rememberMeConfigProperties;

    public LoginAuthenticationFilter(AuthenticationManager authenticationManager, RedisService redisService, RememberMeServices rememberMeServices, JwtConfigProperties jwtConfigProperties, RememberMeConfigProperties rememberMeConfigProperties) {
        super.setRememberMeServices(rememberMeServices);
        this.authenticationManager = authenticationManager;
        this.redisService = redisService;
        this.jwtConfigProperties = jwtConfigProperties;
        this.rememberMeConfigProperties = rememberMeConfigProperties;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //1.获取请求体内容
        String body = RequestUtil.getBody(request);

        //2.转换 json
        JSONObject json = JSONUtil.toBean(body, JSONObject.class);

        //3.获取验证码
        String keyCode = String.format("%s%s", RedisKeyConst.REDIS_KEY_LOGIN_CAPTCHA_CODE, json.getStr("code"));

        //4.判断验证码是否过期
        boolean hasCode = redisService.has(keyCode);
        if (!hasCode) {
            ResponseUtil.fail(response, HttpStatus.FORBIDDEN.value(), "验证码过期或不存在！");
            return null;
        }

        //5.获取解密后的用户名、密码
        String username = RsaUtil.getInstance().decryptByPrivateKey(json.getStr(getUsernameParameter()));
        String password = RsaUtil.getInstance().decryptByPrivateKey(json.getStr(getPasswordParameter()));

        //6.验证用户名输入次数
        String lockKey = String.format("%s%s", RedisKeyConst.REDIS_KEY_LOGIN_USER_COUNT, username);
        boolean locked = redisService.lockByTimes(lockKey, 3, 30, TimeUnit.MINUTES);
        if (!locked) {
            ResponseUtil.fail(response, HttpStatus.FORBIDDEN.value(), "操作频繁，请30分钟后再试！");
            return null;
        }

        //7.从redis中删除
        redisService.dels(keyCode, lockKey);

        //8.记住我
        String rememberMe = json.getStr(rememberMeConfigProperties.getLoginParameter());
        request.setAttribute(rememberMeConfigProperties.getRememberMeParameter(), rememberMe);
        log.info("LoginAuthenticationFilter.attemptAuthentication,username:{},rememberMe:{}", username, rememberMe);

        return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
    }

    /**
     * 认证成功
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        log.info("LoginAuthenticationFilter.successfulAuthentication.authResult:{}", authResult.getPrincipal().toString());
        //1.用户信息
        LoginUserDetail loginUserDetail = (LoginUserDetail) authResult.getPrincipal();

        //2.移除权限信息，减少token的长度
        loginUserDetail.setAuthorities(null);

        //3.记住我
        this.getRememberMeServices().loginSuccess(request, response, authResult);

        //4.响应头token
        response.addHeader(jwtConfigProperties.getHeader(), JwtUtil.createToken(loginUserDetail));
        ResponseUtil.success(response, "登录成功！");
    }

    /**
     * 认证失败
     *
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        log.error("LoginAuthenticationFilter.attemptAuthentication.message:{}", failed.getMessage());
        //1.记住我
        this.getRememberMeServices().loginFail(request, response);
        //2.响应失败
        ResponseUtil.fail(response, HttpStatus.FORBIDDEN.value(), "用户名或密码错误！");
    }
}
